#region Imported Namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.IO;
using System.Text;

using BO = PHSRAG.Insight21.BusinessObjects;
using Utility = PHSRAG.Utility;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using DataAccess = PHSRAG.Utility.DataAccess;
using PHSRAG.Insight21.FormsManagement;
using PHSRAG.Insight21.Humans;
using PHSRAG.Insight21.CDS;
using PHSRAG.Insight21.Context;

using ibex4;
#endregion

namespace PHSRAG.Insight21.COI
{
	/// <summary>
	/// This class abstracts the notion of a Conflict of Interest Disclosure.
	/// A COI object holds the details of a disclosure like status, submitted/reviewed/notified dates,
	/// PolicyQuestion, OtherActivity and flags for different kinds of activities.
	/// </summary>
	public class ConflictOfInterest : BO.SmartBusinessObject, System.ICloneable
	{
		#region Constants
		private const string coiKey = "COI_B57BF6B1-BAAC-4432-A696-2CF6467C558E";
		private const string ColumnId = "Id";
		private const string ColumnAdmPersonId = "AdmPersonId";
		private const string ColumnFwkDomainUserId = "FwkDomainUserId";
		private const string ColumnStatus = "Status";
		private const string ColumnLastNotified = "LastNotified";
		private const string ColumnLastModified = "LastModified";
		private const string ColumnDateSubmitted = "DateSubmitted";
		private const string ColumnDateReviewed = "DateReviewed";
		private const string ColumnHasOutsideActivities = "HasOutsideActivities";
		private const string ColumnHasEquityInterests = "HasEquityInterests";
		private const string ColumnHasFamilyAssociations = "HasFamilyAssociations";
		private const string ColumnHasRoyalties = "Royalty";
		private const string ColumnOtherActivity = "OtherActivity";
		private const string ColumnIsPolicyViolated = "PolicyViolation";
		private const string ColumnAggregateEquity = "AggregateEquity";
		private const string ColumnDocumentId = "DocumentId";
		private const string ColumnReportingPeriodId = "ReportingPeriodId";
		private const string ColumnReportingPeriod = "ReportingPeriod";
        private const string ColumnCOITypeId = "COITypeId";
		#endregion

		#region Instance Variables
		private int admPersonId;
		private int fwkDomainUserId;
		private string status;
		private DateTime lastNotified;
		private DateTime lastModified;
		private DateTime dateSubmitted;
		private DateTime dateReviewed;
		private int aggregateEquity;
		private char hasOutsideActivities;
		private char hasEquityInterests;
		private char hasFamilyAssociations;
		private char hasRoyalties;
		private char hasOtherActivity;
		private bool isPolicyViolated;
		private ArrayList questionnaireAnswers;
		private bool validated;
		private int docId;
		private int reportingPeriodId;
		private string reportingPeriod;
		private Document report;
        private int COITypeId;

		/// <summary>
		/// Used while saving COI. Updates the date specified by this constant with the database server timestamp
		/// </summary>
		private Constants.COIDateType dateToUpdate = Constants.COIDateType.None;
		#endregion

		#region Statics
		/// <summary>
		/// Key used to place and/or retrieve this object from the cache.
		/// </summary>
		public static string COIKey
		{
			get { return coiKey; }
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets the person id responsible for filling out the COI
		/// </summary>
		public int AdmPersonId
		{
			get { return admPersonId; }
			set { SetDirty(ref admPersonId, value); }
		}


		/// <summary>
		/// Gets or sets the domain user id of the person who filled up the COI.
		/// 
		/// External users do not have access to Insight. As such an internal user
		/// fills up the COI on the external users behalf. 
		/// </summary>
		public int FwkDomainUserId
		{
			get { return fwkDomainUserId; }
			set { SetDirty(ref fwkDomainUserId, value); }
		}


		/// <summary>
		/// Gets or sets the status of the COI
		/// </summary>
		public String Status
		{
			get { return status; }
			set { SetDirty(ref status, value); }
		}


		/// <summary>
		/// Gets or sets the last notified date
		/// </summary>
		public DateTime LastNotified
		{
			get { return lastNotified; }
			set { SetDirty(ref lastNotified, value); }
		}


		/// <summary>
		/// Gets or sets the last modified date
		/// </summary>
		public DateTime LastModified
		{
			get { return lastModified; }
			set { SetDirty(ref lastModified, value); }
		}


		/// <summary>
		/// Gets or sets the date on which COI is submitted
		/// </summary>
		public DateTime DateSubmitted
		{
			get { return dateSubmitted; }
			set { SetDirty(ref dateSubmitted, value); }
		}


		/// <summary>
		/// Gets or sets the date on which the COI is reviewed
		/// </summary>
		public DateTime DateReviewed
		{
			get { return dateReviewed; }
			set { SetDirty(ref dateReviewed, value); }
		}


		/// <summary>
		/// Gets or sets the aggregate equity disclosed by the user
		/// </summary>
		public int AggregateEquity
		{
			get { return aggregateEquity; }
			set { SetDirty(ref aggregateEquity, value); }
		}


		/// <summary>
		/// Gets or sets a flag that indicates whether or not the user has outside activities
		/// </summary>
		public char HasOutsideActivities
		{
			get { return hasOutsideActivities; }
			set { SetDirty(ref hasOutsideActivities, value); }
		}


		/// <summary>
		/// Gets or sets a flag that indicates whether or not the user has equity interests
		/// </summary>
		public char HasEquityInterests
		{
			get { return hasEquityInterests; }
			set { SetDirty(ref hasEquityInterests, value); }
		}


		/// <summary>
		/// Gets or sets a flag that indicates whether or not the user's family has associations
		/// </summary>
		public char HasFamilyAssociations
		{
			get { return hasFamilyAssociations; }
			set { SetDirty(ref hasFamilyAssociations, value); }
		}

		/// <summary>
		/// Gets or sets a flag that indicates whether or not the user has roylaties
		/// </summary>
		public char HasRoyalties
		{
			get { return hasRoyalties; }
			set { SetDirty(ref hasRoyalties, value); }
		}


		/// <summary>
		/// Gets or sets a flag that indicates whether or not the disclosure statements have violated any policies
		/// </summary>
		public bool IsPolicyViolated
		{
			get { return isPolicyViolated; }
			set { SetDirty(ref isPolicyViolated, value); }
		}


		/// <summary>
		/// Gets or sets the questionnaire answers
		/// </summary>
		public ArrayList QuestionnaireAnswers
		{
			get
			{
				if (questionnaireAnswers == null)
					questionnaireAnswers = new ArrayList();
				return questionnaireAnswers;
			}
			set { questionnaireAnswers = value; }
		}


		/// <summary>
		/// Gets or sets a flag that indicates whether or not the COI is validated
		/// This property does not affect the dirty state of the object as this
		/// flag is only a means to identify whether the user performed the Validation.
		/// 
		/// Based on this flag the validation errors, if any, are shown on the submission
		/// page. This is valid as long as the object is in memory.
		/// </summary>
		public bool Validated
		{
			get { return validated; }
			set { validated = value; }
		}


		/// <summary>
		/// Gets or sets the document id of the pdf report for COI
		/// </summary>
		public int DocumentId
		{
			get { return docId; }
			set { SetDirty(ref docId, value); }
		}


		/// <summary>
		/// Gets or sets a flag that indicates whether or not the user has other activities
		/// 1. Staffed on any protocol
		/// 2. Has an agreement within Insight
		/// </summary>
		public char HasOtherActivity
		{
			get { return hasOtherActivity; }
			set { SetDirty(ref hasOtherActivity, value); }
		}

		/// <summary>
		/// Gets or sets the COI Reporting Period Id
		/// </summary>
		public int ReportingPeriodId
		{
			get { return reportingPeriodId; }
			set { SetDirty(ref reportingPeriodId, value); }
		}
		/// <summary>
		/// Gets or sets the COI Reporting Period
		/// </summary>
		public string ReportingPeriod
		{
			get { return reportingPeriod; }
			set { reportingPeriod = value; }
		}

		public Document Report
		{
			get
			{
				if (docId == 0)
					return null;
				Document doc = new Document(CachePolicy);
				doc.Load(Utility.DataAccess.ConnectionString, docId);
				return doc;
			}
			set
			{
				report = value;
			}
		}

        public int COIType
        {
            get
            {
                if ((int)COITypeId == (int)Constants.COIType.RealTimeUpdate)
                    return COITypeId;
                else
                    return (int)Constants.COIType.AnnualCycle;
            }
            set
            {
                COITypeId = value;
                
            }
        }

        public bool IsCOIReOpened
        {
            get
            {
                //DataTable dtCOIReOpened = DataAccess.GetDataTable(DataAccess.ConnectionString, "", DataAccess.BuildSqlParameterArray());
                int x = Convert.ToInt32(DataAccess.ExecuteScalar(DataAccess.ConnectionString, "GetCOIReOpened",
                        DataAccess.BuildSqlParameterArray("@coiId", SqlDbType.Int, Id)));
                return x > 0;
            }
        }
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new ConflictOfInterest object based on the Cache Policy passed in as a parameter. 
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		public ConflictOfInterest(ICachePolicy cachePolicy)
			: base(cachePolicy)
		{
		}


		/// <summary>
		/// Constructs a new ConflictOfInterest object based on the Cache Policy, person id passed in as parameters. 
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="admPersonId">AdmPerson Id of the user</param>
		public ConflictOfInterest(ICachePolicy cachePolicy, int admPersonId)
			: base(cachePolicy)
		{
			this.status = Constants.COIStatus.Start;
			this.admPersonId = admPersonId;
		}


		/// <summary>
		/// Constructs a new ConflictOfInterest object based on the Cache Policy and the datarow passed in as parameters.
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="dataRow">DataRow representing a COI</param>
		public ConflictOfInterest(ICachePolicy cachePolicy, DataRow dataRow)
			: base(cachePolicy)
		{
			PopulateMembers(dataRow);
		}
		#endregion

		#region SmartBusinessObject methods
		/// <summary>
		/// Cache Key uniquely identifies this object within the cache.
		/// </summary>
		/// <returns>the unique string for this object.</returns>
		public override string CacheKey()
		{
			return COIKey;
		}


		/// <summary>
		/// Cache this instance using the prevalent cache policy.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(COIKey, this);
		}


		/// <summary>
		/// Remove this instance from the cache using the prevalent cache policy.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(COIKey);
		}


		/// <summary>
		/// Loads the COI object with the specified id.
		/// </summary>
		/// <param name="connectionString">Information to connect to the database</param>
		/// <param name="args">COI Id of the document to be loaded</param>
		public override void Load(string connectionString, params object[] args)
		{
			if (args.Length != 1)
				throw new Exception("COI Load expects Id");

			try
			{
				DataSet coiDetails = Utility.DataAccess.GetDataSet(connectionString, "GetCOI",
					Utility.DataAccess.BuildSqlParameterArray("@coiId", SqlDbType.Int, (int)args[0]));

				DataRowCollection rows = coiDetails.Tables[0].Rows;
				if (rows.Count > 0)
					PopulateMembers(rows[0]);
			}
			catch (Exception e)
			{
				throw new Exception("Failed to load COI with the specified id.", e);
			}
		}


        public void AuditCOIChanges(string ActionPerformed)
        {
            COIAudit coiAudit = new COIAudit(CachePolicy);
            coiAudit.COIId = Id;//COI ID
            coiAudit.Action = ActionPerformed;
            coiAudit.AdmPersonId = (int)((UserContext)CachePolicy.GetCachedObject(UserContext.CacheKey)).PersonId;
            coiAudit.Save(DataAccess.ConnectionString);

        }

		/// <summary>
		/// Saves the state of the COI object to the database. 
		/// The stored procedure inserts a new record if the id of the object is 0, 
		/// otherwise the procedure updates the record with the matching COI id
		/// </summary>
		/// <param name="sqlTransaction">The context in which the insert/update should be executed</param>
		protected override void SaveObject(System.Data.SqlClient.SqlTransaction sqlTransaction)
		{
			try
			{
				if (docId == 0 && report != null)
				{
					report.Save(sqlTransaction);
					docId = report.Id;
					report = null;
				}

				SqlParameter[] paramArray = DataAccess.BuildSqlParameterArray(
					"@id", SqlDbType.Int, Id,
					"@admPersonId", SqlDbType.Int, admPersonId,
					"@fwkDomainUserId", SqlDbType.Int, ((FwkDomainUserId == 0) ? (object)DBNull.Value : FwkDomainUserId),
					"@lastNotified", SqlDbType.DateTime, ((LastNotified == DateTime.MinValue) ? (object)DBNull.Value : LastNotified),
					"@dateSubmitted", SqlDbType.DateTime, ((DateSubmitted == DateTime.MinValue) ? (object)DBNull.Value : DateSubmitted),
					"@dateReviewed", SqlDbType.DateTime, ((DateReviewed == DateTime.MinValue) ? (object)DBNull.Value : DateReviewed),
					"@hasOutsideActivities", SqlDbType.Bit, ((hasOutsideActivities != char.MinValue) ? ((hasOutsideActivities == 'Y') ? 1 : 0) : (object)DBNull.Value),
					"@hasEquityInterests", SqlDbType.Bit, ((hasEquityInterests != char.MinValue) ? ((hasEquityInterests == 'Y') ? 1 : 0) : (object)DBNull.Value),
					"@hasFamilyAssociations", SqlDbType.Bit, ((hasFamilyAssociations != char.MinValue) ? ((hasFamilyAssociations == 'Y') ? 1 : 0) : (object)DBNull.Value),
					"@hasRoyalties", SqlDbType.Bit, ((hasRoyalties != char.MinValue) ? ((hasRoyalties == 'Y') ? 1 : 0) : (object)DBNull.Value),
					"@aggregateEquity", SqlDbType.Int, ((AggregateEquity == 0) ? (object)DBNull.Value : AggregateEquity),
					"@policyViolation", SqlDbType.Bit, isPolicyViolated,
					"@otherActivity", SqlDbType.Bit, ((hasOtherActivity != char.MinValue) ? ((hasOtherActivity == 'Y') ? 1 : 0) : (object)DBNull.Value),
					"@status", SqlDbType.VarChar, status,
					"@reportingPeriodId", SqlDbType.Int, (reportingPeriodId == 0) ? (object)DBNull.Value : reportingPeriodId,
					"@documentId", SqlDbType.Int, ((DocumentId == 0) ? (object)DBNull.Value : DocumentId),
					"@dateToUpdate", SqlDbType.Int, (int)dateToUpdate,
                    "@COITypeId", SqlDbType.Int, (int)COIType);

				paramArray[3].Direction = ParameterDirection.InputOutput;
				paramArray[4].Direction = ParameterDirection.InputOutput;
				paramArray[5].Direction = ParameterDirection.InputOutput;


				Id = Convert.ToInt32(DataAccess.ExecuteScalar(sqlTransaction.Connection, sqlTransaction, "SaveCOI",
						paramArray));

				object o = paramArray[3].Value;
				lastNotified = (o == DBNull.Value) ? DateTime.MinValue : (DateTime)o;

				o = paramArray[4].Value;
				dateSubmitted = (o == DBNull.Value) ? DateTime.MinValue : (DateTime)o;

				o = paramArray[5].Value;
				dateReviewed = (o == DBNull.Value) ? DateTime.MinValue : (DateTime)o;

				//Reset dateToUpdate constant to none
				this.dateToUpdate = Constants.COIDateType.None;

				//Save the contained questionnaire answers and the mapping records, if required
				if (questionnaireAnswers != null)
				{
					foreach (QuestionnaireAnswer questAnswer in questionnaireAnswers)
					{
						int questAnswerIdBeforeSave = questAnswer.Id;
						questAnswer.Save(sqlTransaction);
						if (questAnswerIdBeforeSave == 0)
						{
							Utility.DataAccess.ExecuteScalar(sqlTransaction.Connection, sqlTransaction, "SaveCOIQuestionnaireAnswerMap",
								Utility.DataAccess.BuildSqlParameterArray(
								"@coiId", SqlDbType.Int, Id
								, "@questionnaireAnswerId", SqlDbType.Int, questAnswer.Id
								));
						}
					}
				}

			}
			catch (Exception e)
			{
				throw new Exception("Failed to save the COI", e);
			}
		}


		/// <summary>
		/// Deletes the COI from the database.
		/// Note: Method not implemented
		/// </summary>
		/// <param name="sqlTransaction">The transaction context in which the SQL statement is to be executed</param>
		protected override void DeleteObject(System.Data.SqlClient.SqlTransaction sqlTransaction)
		{
			throw new Exception("Not implemented");
		}


		/// <summary>
		/// Determines if the object is dirty based on whether or not atleast one of its properties have changed or any of its contained objects have changed
		/// </summary>
		/// <returns>true - if the object is dirty or any of its contained objects are dirty, false - if not.</returns>
		public override bool IsDirty()
		{
			if (Dirty)
				return true;

			if (questionnaireAnswers != null)
			{
				foreach (QuestionnaireAnswer answer in questionnaireAnswers)
				{
					if (answer.IsDirty())
						return true;
				}
			}

			return false;
		}


		/// <summary>
		/// Overload to the Save method.
		/// This method updates the COI along with the specified date.
		/// </summary>
		/// <param name="connectionString">Information to connect to the database</param>
		/// <param name="dateToUpdate">COIDateType enumeration</param>
		public void Save(string connectionString, Constants.COIDateType dateToUpdate)
		{
			Dirty = true;
			Save(connectionString, dateToUpdate, null);
		}


		/// <summary>
		/// Overload to the Save method.
		/// This method generates a pdf, updates the COI along with the specified date and ties the pdf with the COI.
		/// </summary>
		/// <param name="connectionString">Information to connect to the database</param>
		/// <param name="dateToUpdate">COIDateType enumeration</param>
		/// <param name="appPath">Application path</param>
		public void Save(string connectionString, Constants.COIDateType dateToUpdate, string appPath)
		{
			if (IsDirty())
			{
				SqlConnection connection = new SqlConnection(connectionString);

				// create a connection and open it.
				connection.Open();

				// create a transaction on the connection to be passed around.
				SqlTransaction transaction = connection.BeginTransaction(Assembly.GetExecutingAssembly().GetName().Name);

				try
				{
					this.dateToUpdate = dateToUpdate;
					dirty = true;
					base.Save(transaction);
					if (appPath != null)
					{
						report = ConflictOfInterest.GetReport(CachePolicy, transaction, appPath, Id);
						dirty = true;
						base.Save(transaction);
					}


					// commit the SQL transaction.
					transaction.Commit();
				}
				catch
				{
					// rollback the transaction if it exists.
					if (transaction != null)
					{
						transaction.Rollback();
					}
					throw;
				}
				finally
				{

					// do some cleanup.
					if (connection.State != ConnectionState.Closed)
						connection.Close();
				}
			}
			Dirty = false;

		}

		#endregion

		#region Private methods
		/// <summary>
		/// Populates instance fields with corresponding values in the datarow
		/// </summary>
		/// <param name="dataRow">DataRow holding the values to be assigned to instance variables</param>
		private void PopulateMembers(DataRow dataRow)
		{
			Id = (int)dataRow[ColumnId];
			admPersonId = (int)dataRow[ColumnAdmPersonId];

			Object dbValue = dataRow[ColumnFwkDomainUserId];
			fwkDomainUserId = (dbValue == DBNull.Value) ? 0 : (int)dbValue;

			status = (string)dataRow[ColumnStatus];

			dbValue = dataRow[ColumnLastNotified];
			lastNotified = (dbValue == DBNull.Value) ? DateTime.MinValue : (DateTime)dbValue;

			dbValue = dataRow[ColumnLastModified];
			lastModified = (dbValue == DBNull.Value) ? DateTime.MinValue : (DateTime)dbValue;

			dbValue = dataRow[ColumnDateSubmitted];
			dateSubmitted = (dbValue == DBNull.Value) ? DateTime.MinValue : (DateTime)dbValue;

			dbValue = dataRow[ColumnDateReviewed];
			dateReviewed = (dbValue == DBNull.Value) ? DateTime.MinValue : (DateTime)dbValue;

			dbValue = dataRow[ColumnHasOutsideActivities];
			hasOutsideActivities = (dbValue == DBNull.Value) ? char.MinValue : (((bool)dbValue) ? 'Y' : 'N');

			dbValue = dataRow[ColumnHasEquityInterests];
			hasEquityInterests = (dbValue == DBNull.Value) ? char.MinValue : (((bool)dbValue) ? 'Y' : 'N');

			dbValue = dataRow[ColumnHasFamilyAssociations];
			hasFamilyAssociations = (dbValue == DBNull.Value) ? char.MinValue : (((bool)dbValue) ? 'Y' : 'N');

			dbValue = dataRow[ColumnHasRoyalties];
			hasRoyalties = (dbValue == DBNull.Value) ? char.MinValue : (((bool)dbValue) ? 'Y' : 'N');

			dbValue = dataRow[ColumnAggregateEquity];
			aggregateEquity = (dbValue == DBNull.Value) ? 0 : (int)dbValue;

			dbValue = dataRow[ColumnReportingPeriodId];
			reportingPeriodId = (dbValue == DBNull.Value) ? 0 : (int)dbValue;

			dbValue = dataRow[ColumnReportingPeriod];
			reportingPeriod = (dbValue == DBNull.Value) ? string.Empty: (string)dbValue;

			dbValue = dataRow[ColumnOtherActivity];
			hasOtherActivity = (dbValue == DBNull.Value) ? char.MinValue : (((bool)dbValue) ? 'Y' : 'N');

			isPolicyViolated = (bool)dataRow[ColumnIsPolicyViolated];

			dbValue = dataRow[ColumnDocumentId];
			docId = (dbValue == DBNull.Value) ? 0 : (int)dbValue;

            dbValue = dataRow[ColumnCOITypeId];
            COITypeId = (dbValue == DBNull.Value) ? 1 : (int)dbValue;
		}


		/// <summary>
		/// Saves the Log Item for the required questionnaireitem and the mapping record to the database
		/// </summary>
		/// <param name="questionnaireItem">Questionnaire Item object</param>
		/// <param name="questionnaireAnswer">Questionnaire Answer to which this log pertains</param>
		private void SaveLogItem(QuestionnaireItem questionnaireItem, QuestionnaireAnswer questionnaireAnswer)
		{
			try
			{
				LogItem logItem = new LogItem(CachePolicy);

				logItem.Category = string.Empty;
				logItem.Type = questionnaireAnswer.Name;
				logItem.QuestionnaireVersionId = (int)questionnaireItem.VersionId;
				logItem.QuestionnaireAnswerId = questionnaireAnswer.Id;
				logItem.Completed = false;
				logItem.Description = (questionnaireItem.ValidationMessage == string.Empty) ? (string)questionnaireItem.Name : (string)questionnaireItem.ValidationMessage;
				logItem.QuestionName = (string)questionnaireItem.Name;
				logItem.Answer = (string)questionnaireItem.ParentValue;

				SaveLogItem(logItem);
			}
			catch (Exception e)
			{
				throw new Exception("Failed to save the LogItem.", e);
			}
		}


		/// <summary>
		/// Loads all questionnaire answers for COI.
		/// </summary>
		/// <param name="cachePolicy">Prevalent cache policy</param>
		/// <returns>Returns the collection of QuestionnaireAnswer objects</returns>
		private ArrayList GetQuestionnaireAnswers(ICachePolicy cachePolicy)
		{

			ArrayList questionnaireAnswers = new ArrayList();
			try
			{
				DataSet answersDataSet = Utility.DataAccess.GetDataSet(Utility.DataAccess.ConnectionString,
					"GetCOIQuestionnaireAnswers",
					Utility.DataAccess.BuildSqlParameterArray
					("@coiId", SqlDbType.Int, Id));
				if (answersDataSet.Tables.Count > 0)
				{
					// create a new Answer for each.
					DataTable table = answersDataSet.Tables[0];

					for (int i = 0, j = table.Rows.Count; i < j; ++i)
					{
						questionnaireAnswers.Add(new QuestionnaireAnswer(cachePolicy, table.Rows[i]));
					}
				}
				else
				{
					throw new Exception("GetCOIQuestionnaireAnswers stored procedure returned incorrect results");
				}
			}
			catch (Exception ex)
			{
				throw new Exception("Failed to get the QuestionnaireAnswers for the COI", ex);
			}
			return questionnaireAnswers;
		}


		/// <summary>
		/// 
		/// </summary>
		private string Serialize(ICachePolicy cachePolicy, string logoPath)
		{
			string connection = Utility.DataAccess.GetConnectionString();

			//				DataTable dt=Utility.DataAccess.GetDataTable( connection,
			//					"GetPDFHeaderDetails",
			//					Utility.DataAccess.BuildSqlParameterArray(
			//					"@protocolProcessId", SqlDbType.Int, protocolProcessId));
			//				DataRow dr=dt.Rows[0];
			FormsManagement.Serialization.Forms forms = new PHSRAG.Insight21.FormsManagement.Serialization.Forms();
			//				forms.Title=(DBNull.Value==dr["Title"])? string.Empty:dr["Title"].ToString();
			//				forms.PIName=(DBNull.Value==dr["PIName"])?string.Empty:dr["PIName"].ToString();
			//				forms.ProtocolNo=(DBNull.Value==dr["ProtocolNo"])?string.Empty:dr["ProtocolNo"].ToString();
			//				forms.Sponsor=(DBNull.Value==dr["Sponsor"])?string.Empty:dr["Sponsor"].ToString();
			//				forms.ApprovedDate = (DBNull.Value == dr["ApprovedDate"]) ? string.Empty : ((DateTime)dr["ApprovedDate"]).ToString("MMMM dd, yyyy");
			//				forms.ExpiryDate = (DBNull.Value == dr["ExpiryDate"]) ? string.Empty : ((DateTime)dr["ExpiryDate"]).ToString("MMMM dd, yyyy");
			//				forms.Today = (DBNull.Value == dr["Today"]) ? string.Empty : ((DateTime)dr["Today"]).ToString("MMMM dd, yyyy");
			//				forms.LogoLocation=	logoPath;
			//				forms.Type=(DBNull.Value==dr["Type"])? string.Empty:dr["Type"].ToString();	
			//				string formsId=forms.FormsId=(DBNull.Value==dr["FormsID"])? string.Empty:dr["FormsID"].ToString();

			ArrayList questionnaireAnswers = GetQuestionnaireAnswers(cachePolicy);
			Hashtable organizedForms = new Hashtable();
			string sponsorName = string.Empty;

			foreach (QuestionnaireAnswer qA in questionnaireAnswers)
			{
				QuestionnaireVersion questionnaireVersion = new QuestionnaireVersion(cachePolicy);
				questionnaireVersion.Load(connection, (qA.QuestionnaireVersionId));

				Questionnaire questionnaire = new Questionnaire(cachePolicy);
				questionnaire.Load(connection, questionnaireVersion.QuestionnaireID);

				FormsManagement.Serialization.Form form = questionnaireVersion.GetForm(qA);
				forms.FormCollection.Add(form);
			}

			return FormsManagement.Serialization.QuestionnaireSerializer.Serialize(forms);

		}
		#endregion

		#region Public methods
		/// <summary>
		/// Save the Log Items and the mapping records to the database
		/// </summary>
		/// <param name="logItem">The log item object that is to be saved</param>
		public void SaveLogItem(LogItem logItem)
		{
			SqlConnection insightConn = null;
			SqlTransaction insightTrans = null;
			try
			{
				insightConn = new SqlConnection(DataAccess.ConnectionString);
				insightConn.Open();
				insightTrans = insightConn.BeginTransaction();

				logItem.Save(insightTrans);

				Utility.DataAccess.ExecuteScalar(insightTrans.Connection, insightTrans, "SaveCOILogItemMap",
					Utility.DataAccess.BuildSqlParameterArray(
					"@coiId", SqlDbType.Int, Id
					, "@logItemId", SqlDbType.Int, logItem.Id
					));
				insightTrans.Commit();


			}
			catch (Exception e)
			{
				// rollback the transaction if it exists.
				if (insightTrans != null)
				{
					insightTrans.Rollback();
				}
				throw new Exception("Failed to save the LogItem.", e);
			}
			finally
			{
				if (insightConn.State != ConnectionState.Closed)
					insightConn.Close();
			}

		}


		/// <summary>A function to check whether or not the required questions in a questionnaire are answered. (For top-level questions)</summary>
		/// <param name="qI">Questionnaire Item</param>
		/// <param name="parentValue">Parent value of the questionnaire Item</param>
		/// <param name="questionnaireAnswer">Questionnaire answer</param>
		/// <param name="qV">Questionnaire version corresponding to the answer</param>
		public void CheckRequiredItems(QuestionnaireItem qI, QuestionnaireAnswer questionnaireAnswer, QuestionnaireVersion qV)
		{
			ArrayList itemAnswers = questionnaireAnswer.GetAnswers((string)qI.Name);

			if (0 == itemAnswers.Count && qI.Required)
			{
				SaveLogItem(qI, questionnaireAnswer);
			}
			else
			{
				foreach (Answer itemAnswer in itemAnswers)
				{
					CheckRequiredItems(qI, itemAnswer, questionnaireAnswer, qV);
				}
			}
		}


		/// <summary>
		/// A recursive function to check whether or not the required questions in a questionnaire are answered. (For non-top-level questions)
		/// </summary>
		/// <param name="qI">Question whose child questions have to be verified</param>
		/// <param name="qIAnswer">Question's Answer</param>
		/// <param name="questionnaireAnswer">QuestionnaireAnswer</param>
		/// <param name="qV">Questionnaire version corresponding to the answer</param>
		private void CheckRequiredItems(QuestionnaireItem qI, Answer qIAnswer, QuestionnaireAnswer questionnaireAnswer, QuestionnaireVersion qV)
		{
			//scan thru all the childquestions whose parentvalue field matches the current answer
			//or if the current question is a multiselect or dropdownmultiselect question
			foreach (QuestionnaireItem child in qV.GetItems(qI.Name).Values)
			{
				//if child is required and either its parentvalue condition is met or its parent is any of the multiselect questions
				//then check if the child is answered
				if (child.Required && (qIAnswer.Value == child.ParentValue
										|| qI.Type == QuestionnaireItem.MultiSelectItemType
										|| qI.Type == QuestionnaireItem.DropDownMultiSelectItemType
										|| qI.Type == QuestionnaireItem.CustomValueSelectorItemType))
				{
					bool itemAnswered = false;
					if (qIAnswer.ChildAnswers != null)
					{
						foreach (Answer childAnswer in qIAnswer.ChildAnswers)
						{
							if (childAnswer.Name == child.Name)
							{
								itemAnswered = true;
								break;
							}
						}
					}
					if (!itemAnswered)
					{
						SaveLogItem(child, questionnaireAnswer);
					}
				}
				if (qIAnswer.ChildAnswers != null)
				{
					//check required items for each child and foreach of its answers.
					foreach (Answer childAnswer in qIAnswer.ChildAnswers)
					{
						if (childAnswer.Name == child.Name)
						{
							CheckRequiredItems(child, childAnswer, questionnaireAnswer, qV);
						}
					}
				}
			}
		}


		/// <summary>
		/// Removes all the logItems tied with the COI
		/// </summary>
		public void RemoveLogItems()
		{
			try
			{
				Utility.DataAccess.ExecuteNonQuery(DataAccess.GetConnectionString(), "RemoveLogItemsForCOI",
					Utility.DataAccess.BuildSqlParameterArray(
					"@coiId", SqlDbType.Int, Id));
			}
			catch (Exception e)
			{
				throw new Exception("Failed to remove log items.", e);
			}
		}


		/// <summary>
		/// Returns the serialized form of COI
		/// </summary>
		/// <returns>Xml string</returns>
		public static Document GetReport(ICachePolicy cachePolicy, string connectionString, string appPath, int coiId)
		{
			SqlConnection connection = new SqlConnection(connectionString);

			// create a connection and open it.
			connection.Open();

			// create a transaction on the connection to be passed around.
			SqlTransaction transaction = connection.BeginTransaction(Assembly.GetExecutingAssembly().GetName().Name);

			try
			{
				Document doc = GetReport(cachePolicy, transaction, appPath, coiId);
				transaction.Commit();
				return doc;
			}
			catch
			{
				// rollback the transaction if it exists.
				if (transaction != null)
				{
					transaction.Rollback();
				}
				throw;
			}
			finally
			{
				// do some cleanup.
				if (connection.State != ConnectionState.Closed)
					connection.Close();
			}
		}


		/// <summary>
		/// Returns a pdf containing the questions and answers
		/// </summary>
		/// <param name="cachePolicy">The prevalent cachepolicy</param>
		/// <param name="connectionString">Info to connect to the database</param>
		/// <param name="appPath">Application path</param>
		/// <param name="coiId">Id of COI</param>
		/// <returns>A pdf document</returns>
		public Document GetDisclosure(ICachePolicy cachePolicy, string connectionString, string appPath)
		{
			MemoryStream xmlStream = null;
			FileStream xslStream = null;
			MemoryStream pdfStream = null;
			try
			{
				FODocument foDocument = new FODocument();

				string xslFilePath = Path.Combine(appPath, Utility.Settings.GetKeyValue("COIDisclosureXSLFOFileName", "COIDisclosure.xsl"));
				ibex4.licensing.Generator.LicenseFileLocation = Path.Combine(appPath, @"..\Research\" + Humans.Constants.Ibex.LicenseFileName);

				string logoPath = Path.Combine(appPath, "..");
				logoPath = Path.Combine(logoPath, Utility.Settings.GetKeyValue("PartnersLogo", ""));


				string coiXML = Serialize(cachePolicy, logoPath);

				xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(coiXML));
				xslStream = new FileStream(xslFilePath, FileMode.Open, FileAccess.Read);
				pdfStream = new MemoryStream();
				using (xslStream)
				{
					foDocument.generate(xmlStream, xslStream, pdfStream, false);
				}

				Document doc = new Document(cachePolicy);
				doc.Name = "COIDisclosure.pdf";
				doc.Blob = pdfStream.ToArray();
				doc.DateCreated = DateTime.Now;

				return doc;
			}
			catch (Exception e)
			{
				throw new Exception("Error generating the pdf.", e);
			}
			finally
			{
				if (xmlStream != null)
					xmlStream.Close();
				if (xslStream != null)
					xslStream.Close();
				if (pdfStream != null)
					pdfStream.Close();
			}
		}


		/// <summary>
		/// Gets the individual report
		/// </summary>
		/// <returns>Document object</returns>
		public static Document GetReport(ICachePolicy cachePolicy, SqlTransaction transaction, string appPath, int coiId)
		{
			MemoryStream xmlStream = null;
			FileStream xslStream = null;
			MemoryStream pdfStream = null;
			try
			{
				//UserAgent.PageWidth = "297mm";
				//UserAgent.PageHeight = "210mm";

				FODocument foDocument = new FODocument();
				foDocument.Settings.PageHeight = "210mm";
				foDocument.Settings.PageWidth = "297mm";

				string xslFilePath = Path.Combine(appPath, Utility.Settings.GetKeyValue("COIIndividualXSLFOFileName", "COIIndividualReport.xsl"));
				ibex4.licensing.Generator.LicenseFileLocation = Path.Combine(appPath, @"..\Research\" + Humans.Constants.Ibex.LicenseFileName);

				string logoPath = Path.Combine(appPath, "..");
				logoPath = Path.Combine(logoPath, Utility.Settings.GetKeyValue("PartnersLogo", ""));

				DataTable coiDetails = Utility.DataAccess.GetDataSet(transaction.Connection, transaction, "GetCOIXML",
					Utility.DataAccess.BuildSqlParameterArray("@coiId", SqlDbType.Int, coiId)).Tables[0];

				DataRowCollection rows = coiDetails.Rows;
				string coiXML = string.Empty;
				if (rows.Count > 0)
					coiXML = rows[0][0].ToString();

				xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(coiXML));
				xslStream = new FileStream(xslFilePath, FileMode.Open, FileAccess.Read);
				pdfStream = new MemoryStream();
				using (xslStream)
				{
					foDocument.generate(xmlStream, xslStream, pdfStream, false);
				}

				Document doc = new Document(cachePolicy);
				doc.Name = "COIReport.pdf";
				doc.Blob = pdfStream.ToArray();
				doc.DateCreated = DateTime.Now;

				return doc;
			}
			catch (Exception e)
			{
				throw new Exception("Error generating the report.", e);
			}
			finally
			{
				if (xmlStream != null)
					xmlStream.Close();
				if (xslStream != null)
					xslStream.Close();
				if (pdfStream != null)
					pdfStream.Close();
			}
		}


		/// <summary>
		/// Returns all the questionnaires left joined with available questionnaire answers
		/// </summary>
		/// <returns>All questionnaires that are answered or need to be answered</returns>
		public DataTable GetCompleteQuestionnaireAnswerList()
		{
			try
			{
				return Utility.DataAccess.GetDataTable(Utility.DataAccess.ConnectionString,
					"GetCOICompleteQuestionnaireAnswerList",
					Utility.DataAccess.BuildSqlParameterArray
					("@coiId", SqlDbType.Int, Id));
			}
			catch (Exception e)
			{
				throw new Exception("Failed to load Questionnaire Answers for COI specified.", e);
			}

		}


		/// <summary>
		/// Loads all the questionnaire answers tied to the COI
		/// </summary>
		/// <param name="cachePolicy"></param>
		public void LoadQuestionnaireAnswers()
		{
			questionnaireAnswers = GetQuestionnaireAnswers(CachePolicy);
		}


		/// <summary>
		/// Removes the Questionnaire Answer and the mapping record
		/// </summary>
		/// <param name="questionnaireAnswerId">Id of the Questionnaire Answer to be removed</param>
		public void RemoveQuestionnaireAnswer(int questionnaireAnswerId)
		{
			SqlConnection connection = null;
			SqlTransaction transaction = null;
			try
			{
				connection = new SqlConnection(Utility.DataAccess.ConnectionString);

				connection.Open();

				transaction = connection.BeginTransaction(Assembly.GetExecutingAssembly().GetName().Name);

				Utility.DataAccess.ExecuteNonQuery(transaction.Connection, transaction, "DeleteCOIQuestionnaireAnswerMap", Utility.DataAccess.BuildSqlParameterArray(
					"@coiId", SqlDbType.Int, Id,
					"@questionnaireAnswerId", SqlDbType.Int, questionnaireAnswerId));

				Utility.DataAccess.ExecuteNonQuery(transaction.Connection, transaction, "DeleteQuestionnaireAnswer", Utility.DataAccess.BuildSqlParameterArray(
					"@id", SqlDbType.Int, questionnaireAnswerId));

				transaction.Commit();

			}
			catch (Exception e)
			{
				if (transaction != null)
					transaction.Rollback();
				throw new Exception("Failed to remove questionnaire answer.", e);
			}
			finally
			{
				if (connection != null && connection.State != ConnectionState.Closed)
					connection.Close();
			}
		}


		/// <summary>
		/// Sets the other activities flag. Used at the time of submission.
		/// Other Activities involve
		/// 1. Staffed on a protocol
		/// 2. Tied to an agreement
		/// </summary>
		public void UpdateOtherActivities()
		{
			try
			{
				hasOtherActivity = (Convert.ToBoolean(Utility.DataAccess.ExecuteScalar(Utility.DataAccess.ConnectionString,
				   "GetOtherActivities",
				   Utility.DataAccess.BuildSqlParameterArray
					("@admPersonId", SqlDbType.Int, admPersonId))) ? 'Y' : 'N');
			}
			catch (Exception e)
			{
				throw new Exception("Failed to get other activities", e);
			}

		}
		#endregion

		#region ICloneable Members
		/// <summary>
		/// Explicit interface method implementation - available for 
		/// clients of ICloneable, but invisible to other
		/// clients of ConflictOfInterest class
		/// </summary>
		/// <returns>ConflictOfInterest clone as a generic object</returns>
		object ICloneable.Clone()
		{
			return Clone();
		}


		/// <summary>
		/// Deep Copies the ConflictOfInterest object
		/// </summary>
		/// <returns>A clone of the current ConflictOfInterest</returns>
		public ConflictOfInterest Clone()
		{
			ConflictOfInterest clone = (ConflictOfInterest)MemberwiseClone();
			clone.Id = 0;
			clone.DocumentId = 0;
			clone.QuestionnaireAnswers = new ArrayList();
			foreach (QuestionnaireAnswer qA in questionnaireAnswers)
			{
				clone.QuestionnaireAnswers.Add(qA.Clone(true));
			}

			return clone;
		}
		#endregion
	}
}
